// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <gtest/gtest.h>
#include <climits>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <cantor/parser/parser.h>

int main(int argc, char *argv[]) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

namespace cantor::parser {
    TEST(test_parser, case_replace) {
        ///
        {
            cantor::parser::SqlParser parser;
            //select distict
            std::string sql_replace0 = "replace LOW_PRIORITY into db.table_a(field_a, field_b) values (1, 'test'),"
                    " (2, 'test_2'), (3, 'test_3')";
            parser.parse(sql_replace0);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            std::cout << "location:" << insert_stmt->location.location << " len:" << insert_stmt->location.len <<
                    std::endl;
            ASSERT_TRUE(insert_stmt->is_replace);
            ASSERT_FALSE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);
            ASSERT_EQ(insert_stmt->table_name->db_name.to_string(), "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(2, insert_stmt->columns.size());


            ASSERT_TRUE(insert_stmt->columns[0]->column_name.to_string() == "field_a");

            ASSERT_TRUE(insert_stmt->columns[1]->column_name.to_string() == "field_b");


            ASSERT_EQ(3, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());
            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(2, row_expr1->sub_exprs.size());
            cantor::parser::RowExpr *row_expr2 = insert_stmt->lists[2];
            ASSERT_EQ(2, row_expr2->sub_exprs.size());
            ASSERT_EQ(0, insert_stmt->on_duplicate.size());
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            std::cout << "sql2: ";
            std::cout << insert_stmt->to_string() << std::endl;
            std::cout << insert_stmt->to_json().dump() << std::endl;
        }
    }

    TEST(test_parser, case_update) {
        {
            cantor::parser::SqlParser parser;
            //select distict
            std::string sql_replace0 = "replace LOW_PRIORITY into db.table_a(field_a, field_b) values (1, 'test'),"
                    " (2, 'test_2'), (3, 'test_3')";
            parser.parse(sql_replace0);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            std::cout << "location:" << insert_stmt->location.location << " len:" << insert_stmt->location.len <<
                    std::endl;
            ASSERT_TRUE(insert_stmt->is_replace);
            ASSERT_FALSE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);
            ASSERT_EQ(insert_stmt->table_name->db_name.to_string(), "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(2, insert_stmt->columns.size());


            ASSERT_TRUE(insert_stmt->columns[0]->column_name.to_string() == "field_a");

            ASSERT_TRUE(insert_stmt->columns[1]->column_name.to_string() == "field_b");


            ASSERT_EQ(3, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());
            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(2, row_expr1->sub_exprs.size());
            cantor::parser::RowExpr *row_expr2 = insert_stmt->lists[2];
            ASSERT_EQ(2, row_expr2->sub_exprs.size());
            ASSERT_EQ(0, insert_stmt->on_duplicate.size());
            std::cout << insert_stmt->to_json().dump() << std::endl;
        }
    }

    TEST(test_parser, case_delete) { {
            cantor::parser::SqlParser parser;
            //select distict
            std::string sql_delete0 = "delete low_priority quick ignore from db.table_a"
                    " where filed_a + 3 > 100 or filed_b + 1 < 10 and filed_c *3 > 45"
                    " order by filed_a, filed_b limit 10, 100";
            parser.parse(sql_delete0);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::DeleteStmt));
            auto *delete_stmt = (cantor::parser::DeleteStmt *) parser.result[0];
            std::cout << delete_stmt->to_string() << std::endl;
            ASSERT_TRUE(delete_stmt->is_ignore);
            ASSERT_EQ(delete_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(delete_stmt->is_quick);

            ASSERT_TRUE(typeid(*(delete_stmt->table_name)) == typeid(cantor::parser::TableName));
            auto *from_table = (TableName *) delete_stmt->table_name;
            ASSERT_EQ(from_table->db_name.to_string(), "db");
            ASSERT_EQ(from_table->table_name.to_string(), "table_a");
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            std::cout << "sql2: ";
            std::cout << delete_stmt->to_string() << std::endl;
        } {
            cantor::parser::SqlParser parser;
            //select distict
            std::string sql_delete1 = "delete from db.table_a"
                    " where filed_a + 3 > 100 or filed_b + 1 < 10 and filed_c *3 > 45";
            parser.parse(sql_delete1);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::DeleteStmt));
            auto *delete_stmt = (cantor::parser::DeleteStmt *) parser.result[0];
            std::cout << delete_stmt->to_string() << std::endl;
            ASSERT_FALSE(delete_stmt->is_ignore);
            ASSERT_EQ(delete_stmt->priority, cantor::pb::ast::PE_NO_PRIORITY);
            ASSERT_FALSE(delete_stmt->is_quick);

            ASSERT_TRUE(typeid(*(delete_stmt->table_name)) == typeid(cantor::parser::TableName));
            auto *from_table = (cantor::parser::TableName *) delete_stmt->table_name;
            ASSERT_EQ(from_table->db_name.to_string(), "db");
            ASSERT_EQ(from_table->table_name.to_string(), "table_a");
        } {
            cantor::parser::SqlParser parser;
            //select distict
            std::string sql_delete2 = "delete db.table_a, db.table_b from db.table_a inner join db.table_b"
                    " where filed_a + 3 > 100 or filed_b + 1 < 10 and filed_c *3 > 45";
            parser.parse(sql_delete2);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::DeleteStmt));
            auto *delete_stmt = (cantor::parser::DeleteStmt *) parser.result[0];
            std::cout << delete_stmt->to_string() << std::endl;
            ASSERT_FALSE(delete_stmt->is_ignore);
            ASSERT_EQ(delete_stmt->priority, cantor::pb::ast::PE_NO_PRIORITY);
            ASSERT_FALSE(delete_stmt->is_quick);

            ASSERT_TRUE(typeid(*(delete_stmt->from_table->join_node)) == typeid(cantor::parser::JoinNode));

            ASSERT_EQ(2, delete_stmt->delete_table_list.size());
            cantor::parser::TableName *table_name0 = delete_stmt->delete_table_list[0];
            ASSERT_EQ(table_name0->db_name.to_string(), "db");
            ASSERT_EQ(table_name0->table_name.to_string(), "table_a");
            cantor::parser::TableName *table_name1 = delete_stmt->delete_table_list[1];
            ASSERT_EQ(table_name1->db_name.to_string(), "db");
            ASSERT_EQ(table_name1->table_name.to_string(), "table_b");
        } {
            cantor::parser::SqlParser parser;
            //select distict
            std::string sql_delete2 = "delete db.table_a, db.table_b from db.table_a"
                    " where filed_a + 3 > 100 or filed_b + 1 < 10 and filed_c *3 > 45";
            parser.parse(sql_delete2);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::DeleteStmt));
            auto *delete_stmt = (cantor::parser::DeleteStmt *) parser.result[0];
            std::cout << delete_stmt->to_string() << std::endl;
            ASSERT_FALSE(delete_stmt->is_ignore);
            ASSERT_EQ(delete_stmt->priority, cantor::pb::ast::PE_NO_PRIORITY);
            ASSERT_FALSE(delete_stmt->is_quick);

            ASSERT_TRUE(typeid(*(delete_stmt->from_table->source)) == typeid(cantor::parser::TableSource));
        } {
            cantor::parser::SqlParser parser;
            //select distict
            std::string sql_update0 = "update low_priority ignore db.table_a set filed_a = 10"
                    " where filed_a + 3 > 100 or filed_b + 1 < 10 and filed_c *3 > 45 order by field_a, field_b desc limit 10";
            parser.parse(sql_update0);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::UpdateStmt));
            auto *update_stmt = (cantor::parser::UpdateStmt *) parser.result[0];
            std::cout << update_stmt->to_string() << std::endl;
            ASSERT_TRUE(update_stmt->is_ignore);
            ASSERT_EQ(update_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);

            ASSERT_TRUE(typeid(*(update_stmt->table_refs->source)) == typeid(cantor::parser::TableSource));
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            std::cout << "sql2: ";
            std::cout << update_stmt->to_string() << std::endl;
        } {
            cantor::parser::SqlParser parser;
            //select distict
            std::string sql_truncate = "truncate db_table.a";
            parser.parse(sql_truncate);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::TruncateStmt));
            auto *truncate_stmt = (cantor::parser::TruncateStmt *) parser.result[0];
            std::cout << truncate_stmt->to_string() << std::endl;

            ASSERT_TRUE(typeid(*(truncate_stmt->table_name)) == typeid(cantor::parser::TableName));
        }
    }
} // namespace cantor
